// SFDL3Dlg.cpp : implementation file
//

#include "stdafx.h"
#include "SFDL3.h"
#include "SFDL3Dlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#include "ConfigSheet.h"

#include "ActivityWatcher.h"

/////////////////////////////////////////////////////////////////////////////
// CSFDL3Dlg dialog

CString CSFDL3Dlg::CONFIG_FILE="default.cfg";
CString CSFDL3Dlg::CHAR_ID="default.cfg";

const char STAT_FILE[]="stat.bin";
const char STAT2_FILE[]="stat2.log";

const UINT nIDEvt=870827;
const UINT nElapse=1024;


CSFDL3Dlg::CSFDL3Dlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSFDL3Dlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CSFDL3Dlg)
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	// initialize member

	m_pConfig=NULL;
	m_bUserActive=true;
	m_StartTime=m_LastSwitchTime=m_LastActiveTime=CTime::GetCurrentTime();

	m_bExit=false;

	// read completed section time
	m_CompletedSectionTime=CTimeSpan(0,0,0,0);
	CTime timeSaved;

	CStdioFile fileStat;
	if (fileStat.Open(STAT_FILE, CFile::modeRead))
	{
		try
		{
			CArchive ar(&fileStat, CArchive::load);
			ar>>m_CompletedSectionTime>>timeSaved;
			ar.Close();

			CTimeSpan span=CTime::GetCurrentTime()-timeSaved;
			// if too old, then can't be count into current section
			if (span.GetTotalHours()>4)
			{
				// first back up latest session
				CStdioFile flLog(STAT2_FILE, CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite|CFile::typeText);
				flLog.WriteString(m_CompletedSectionTime.Format("%H : %M : %S")+"\t"
					+timeSaved.Format("%#c")+"\n");
				flLog.Close();

				// set back to zero
				m_CompletedSectionTime=CTimeSpan(0,0,0,0);
			}
		}
		catch(...)
		{
		}
	}
	



	// Load configuration
	CStdioFile file;
	if (file.Open(CONFIG_FILE, CFile::modeRead))
	{
		try
		{
			CArchive ar(&file, CArchive::load);
			m_pConfig=(CConfiguration*) ar.ReadObject(NULL);
			ar.Close();
		}
		catch(...)
		{

		}
	}

	if (m_pConfig==NULL)
	{
		m_pConfig=new CConfiguration();
	}
}

CSFDL3Dlg::~CSFDL3Dlg()
{
	CStdioFile file;
	if(file.Open(CONFIG_FILE, CFile::modeCreate | CFile::modeWrite))
	{
		CArchive ar(&file, CArchive::store);
		ar.WriteObject(m_pConfig);
		ar.Close();
	}
	
	delete m_pConfig;

	CStdioFile fileStat;
	if(fileStat.Open(STAT_FILE, CFile::modeCreate | CFile::modeWrite))
	{
		CArchive ar(&fileStat, CArchive::store);
		ar<<m_CompletedSectionTime<<CTime::GetCurrentTime();
		ar.Close();
	}
}

void CSFDL3Dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSFDL3Dlg)
	DDX_Control(pDX, IDC_MSAGENT, m_AgentCtl);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSFDL3Dlg, CDialog)
	//{{AFX_MSG_MAP(CSFDL3Dlg)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSFDL3Dlg message handlers

BOOL CSFDL3Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// UI Initialization
	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon


	// TODO: Add extra initialization here
	try
	{
		m_AgentCtl.GetCharacters().Load(CHAR_ID, _variant_t(m_pConfig->m_CharacterKeyStr));
	}
	catch(...)
	{
		AfxMessageBox(m_pConfig->m_CharacterKeyStr + " can't be found", MB_OK);
		PostQuitMessage(0);
		return TRUE;
	}
	m_Character=m_AgentCtl.GetCharacters().GetItem(CHAR_ID);

	// Support only English
	m_Character.SetLanguageID(0x0409);

	try
	{
		m_Character.SetTTSModeID(m_pConfig->m_TTSModeId);
	}
	catch(...){}

	// Start up
	int scw=GetSystemMetrics(SM_CXSCREEN);
	int sch=GetSystemMetrics(SM_CYSCREEN);

	m_Character.MoveTo((short)(scw*0.618), (short)(sch*0.618), _variant_t());
	m_Character.Show(_variant_t(false));

	OnAgentGreeting();

	// Add commands
	AddCommand("Statistics", OnAgentStatistics);
	AddCommand("Greeting", OnAgentGreeting);
	AddCommand("Config", OnAgentConfig);
	AddCommand("Quit", OnAgentQuit);

	// Set timer
	SetTimer(nIDEvt, nElapse, NULL);

	// Add notifiers
	m_NotifyManager.AddNotifier(new CDrinkNotifier());
	m_NotifyManager.AddNotifier(new CPhysicalActiveNotifier());
	m_NotifyManager.AddNotifier(new CSleepNotifier(m_pConfig));
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSFDL3Dlg::OnAgentConfig()
{
	CConfigSheet sheet(m_pConfig);
	sheet.DoModal();

	DWORD dwErr=GetLastError();
}

void CSFDL3Dlg::OnAgentGreeting()
{
	char* aszWords[]=
	{
		"morning",
		"afternoon",
		"night",
	};

	int hour=CTime::GetCurrentTime().GetHour();
	int idx=(hour<12 ? 0 : (hour<17 ? 1 : 2) );
	
	CString csWord;
	csWord.Format("Good %s!", aszWords[idx]);

	m_Character.Speak(_variant_t(csWord), _variant_t());

	char* aszWordsHappy[]=
	{
		"A briliant new start again!",
			"Enjoy!",
			"Relaxing!",
	};
	m_Character.Speak(_variant_t(aszWordsHappy[idx]), _variant_t());
}

void CSFDL3Dlg::AddCommand(CString csName, pCmdHandler pHdler )
{
	m_Character.GetCommands().Add(_bstr_t(csName), _variant_t(csName),
		_variant_t(), _variant_t(true), _variant_t(true));

	CmdHandlerEntry entry;
	entry.Name=csName;
	entry.Handler=pHdler;

	m_Hanlders.AddTail(entry);
}

BEGIN_EVENTSINK_MAP(CSFDL3Dlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CSFDL3Dlg)
	ON_EVENT(CSFDL3Dlg, IDC_MSAGENT, 17 /* Command */, OnCommandMsagent, VTS_DISPATCH)
	ON_EVENT(CSFDL3Dlg, IDC_MSAGENT, 7 /* Hide */, OnHideMsagent, VTS_BSTR VTS_I2)
	//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CSFDL3Dlg::OnCommandMsagent(LPDISPATCH UserInput) 
{
	// TODO: Add your control notification handler code here
	UserInput->AddRef();
	CAgentCtlUserInput ui(UserInput);

	for (POSITION p=m_Hanlders.GetHeadPosition();
		p!=NULL;)
	{
		CmdHandlerEntry& entry = m_Hanlders.GetNext(p);

		if (entry.Name==ui.GetName())
		{
			(this->*entry.Handler)();
		}
	}
}

void CSFDL3Dlg::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	if(m_bUserActive)
		m_NotifyManager.Update(&m_Character);

	CTime now=CTime::GetCurrentTime();

	bool bActive=CActivityWatcher::Instance().Update();
	if (bActive)
	{
		// update last active time
		m_LastActiveTime=now;

		// test if the user is just back
		if (!m_bUserActive)
		{
			SwitchToActive();
		}
		// the user has been continuously active
		else
		{
			NotifyForWorkTimeSpan();
		}
	}
	else
	{
		// if it's suitable to think the user is away
		if (m_bUserActive)
		{
			CTimeSpan span=now-m_LastActiveTime;
			if (span.GetTotalMinutes()>m_pConfig->m_DeactiveOnMinutes)
			{
				SwitchToDeactive();
			}
		}
	}
	
	CDialog::OnTimer(nIDEvent);
}

void CSFDL3Dlg::SwitchToActive()
{
	m_bUserActive=true;

	m_LastSwitchTime=CTime::GetCurrentTime();
	if (!m_Character.GetVisible())
	{
		m_Character.Show(_variant_t(false));
	}
	m_Character.Speak(_variant_t("Welcom back!"), _variant_t());
}

void CSFDL3Dlg::SwitchToDeactive()
{
	m_bUserActive=false;

	// Complete of a section
	CTime now=CTime::GetCurrentTime();
	m_CompletedSectionTime+=(now-m_LastSwitchTime);
	m_LastSwitchTime=now;

	if (m_Character.GetVisible())
	{
		m_Character.Hide(_variant_t(false));
	}
}

void CSFDL3Dlg::OnAgentQuit()
{
	m_bExit=true;
	m_Character.StopAll(_variant_t("Speak, Play"));
	SwitchToDeactive();
}


void CSFDL3Dlg::OnHideMsagent(LPCTSTR CharacterID, short Cause) 
{
	if (m_bExit)
	{
		PostQuitMessage(0);
	}
}

void CSFDL3Dlg::GetActiveTimeInfo(CTimeSpan &spanSection, CTimeSpan &spanTotal)
{
	CTime now=CTime::GetCurrentTime();

	spanSection=now-m_LastSwitchTime;
	spanTotal=m_CompletedSectionTime+spanSection;
}

void CSFDL3Dlg::Speak( const CString& szWord )
{
	m_Character.Speak(_variant_t(szWord), _variant_t());
}

void CSFDL3Dlg::NotifyForWorkTimeSpan()
{
	CTimeSpan spanSection;
	CTimeSpan spanTotal;

	GetActiveTimeInfo(spanSection, spanTotal);
	
	if (spanTotal.GetTotalHours()>=m_pConfig->m_MaxDayHours)
	{
		CString csMsg;
		csMsg.Format(
			"As far as I know, today you've worked for %d hours and %d minutes.",
			spanTotal.GetHours(), spanTotal.GetMinutes());
		Speak(csMsg);
		Speak("To achieve a healthy and balanced life");
		Speak("You should stop working today");

		SwitchToDeactive();
		return;
	}

	if (spanSection.GetTotalMinutes()>=m_pConfig->m_MaxContMinutes)
	{
		CString csMsg;
		csMsg.Format(
			"You've been working continuously for %d minutes!", spanSection.GetTotalMinutes());
		Speak(csMsg);
		Speak("It could be much better if you take a break.");
		
		SwitchToDeactive();
	}
}

void CSFDL3Dlg::OnAgentStatistics()
{
	CTimeSpan spanSection;
	CTimeSpan spanTotal;
	
	GetActiveTimeInfo(spanSection, spanTotal);

	CString csMsg;
	csMsg.Format(
		"You've been working continuously for %d minutes.", spanSection.GetTotalMinutes());
	Speak(csMsg);

	csMsg.Format(
		"As far as I know, today you've worked for %d hours and %d minutes.",
		spanTotal.GetHours(), spanTotal.GetMinutes());
	Speak(csMsg);

}
